﻿using System;
using System.Collections.Generic;

namespace NMemcached.Client
{
    /// <summary>
    /// A base class for creating a connection to Memcached.
    /// </summary>
    /// <remarks>
    /// <para>
    /// MemcachedConnection is the abstract base class for connections to Memcached servers. It is not
    /// intended to be represetative of a single server connection but an abstraction of the operations
    /// required to interact with Memcached.
    /// </para>
    /// <para>
    /// The MemcachedConnection class supports the folowing cache operations:
    /// <para>
    /// Storage Commands
    /// </para>
    /// <list type="bullet">
    ///     <item>
    ///         <term>Add</term>
    ///         <description>Inserts a key/value pair into the cache if the data does not already exist.</description>
    ///     </item>
    ///     <item>
    ///         <term>Append</term>
    ///         <description>Adds data to the end of an existing cache entry.</description>
    ///     </item>
    ///     <item>
    ///         <term>Check and Set (CAS)</term>
    ///         <description>Stores data for an existing key only if it has not been updated since the last get operation.</description>
    ///     </item>
    ///     <item>
    ///         <term>Prepend</term>
    ///         <description>Adds data to the begining of an existing cache entry.</description>
    ///     </item>
    ///     <item>
    ///         <term>Replace</term>
    ///         <description>Replaces the value of an existing cache entry with new data.</description>
    ///     </item>
    ///     <item>
    ///         <term>Set</term>
    ///         <description>Inserts an item into the cache overwriting any existing entry with the same key.</description>
    ///     </item>
    /// </list>
    /// <para>
    /// Retrieval Commands
    /// </para>
    /// <list type="bullet">
    ///     <item>
    ///         <term>Get</term>
    ///         <description>Reads one or more entries from the cache.</description>
    ///     </item>
    ///     <item>
    ///         <term>GetWitDetail</term>
    ///         <description>Reads one or more entries from the cache with additional details about the cache entry.</description>
    ///     </item>
    /// </list>
    /// <para>
    /// Other Commands
    /// </para>
    /// <list type="bullet">
    ///     <item>
    ///         <term>Delete</term>
    ///         <description>Removes an item from the cache.</description>
    ///     </item>
    ///     <item>
    ///         <term>Decrement</term>
    ///         <description>Reduces a numeric cache entry by a specified amount.</description>
    ///     </item>
    ///     <item>
    ///         <term>Flush</term>
    ///         <description>Removes all entries from the cache.</description>
    ///     </item>
    ///     <item>
    ///         <term>FlushAll</term>
    ///         <description>Removes all entries from the cache after a specified delay.</description>
    ///     </item>
    ///     <item>
    ///         <term>Increment</term>
    ///         <description>Increases a numeric cache entry by a specified amount.</description>
    ///     </item>
    /// </list>
    /// </para>
    /// </remarks>
    public abstract class MemcachedConnection
    {
        /// <summary>
        /// Initializes a new instance of the MemcachedConnection class
        /// </summary>
        protected MemcachedConnection()
        {
        }

        //Public abstract methods
        /// <summary>
        /// When overridden in a derived class, adds a key/value pair to a Memcached server if the data does not already exist.
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="expiration">The timestamp at wich the entry expires</param>
        /// <returns>The response code of the command sent to the server</returns>
        public abstract ResponseCode Add(string key, object data, DateTime expiration);

        /// <summary>
        /// When overridden in a derived class, adds data to an existing cache entry after the existing data
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <returns>The response code of the command sent to the server</returns>
        public abstract ResponseCode Append(string key, object data);

        /// <summary>
        /// When overridden in a derived class, stores data for an existing key only if it has not been updated since the last get
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="expiration">The timestamp at wich the entry expires</param>
        /// <param name="casUnique">The unique check and store value of an an existing entry</param>
        /// <returns>The response code of the command sent to the server</returns>
        public abstract ResponseCode CheckAndSet(string key, object data, DateTime expiration, ulong casUnique);

        /// <summary>
        /// Closes the connection to the Memcached server
        /// </summary>
        public abstract void Close();

        /// <summary>
        /// When overridden in a derived class, decreases a numeric cached value at the specified key by the specified amount
        /// </summary>
        /// <param name="key">The cache entry to increment</param>
        /// <param name="count">The amount by which the value is decremented</param>
        /// <returns>The new value of the data after the decrement operation was carried out</returns>
        public abstract ulong Decrement(string key, ulong count);

        /// <summary>
        /// When overridden in a derived class, removes a cache entry
        /// </summary>
        /// <param name="key">The key of the entry to be deleted</param>
        /// <returns>The response code of the command sent to the server</returns>
        public abstract ResponseCode Delete(string key);

        /// <summary>
        /// When overridden in a derived class, immediately invalidates all cached items in all servers
        /// </summary>
        /// <remarks>
        /// <para>The "flush_all" command has no response other than OK unless there is an error</para>
        /// </remarks>
        public abstract void FlushAll();

        /// <summary>
        /// When overridden in a derived class, invalidates all cached items in all servers after the specified delay
        /// </summary>
        /// <param name="delay">The amount of time to pause prior to executing the "flush_all" command.</param>
        /// <remarks>
        /// <para>The "flush_all" command has no response other than OK unless there is an error.</para>
        /// </remarks>
        public abstract void FlushAll(TimeSpan delay);

        /// <summary>
        /// When overridden in a derived class, reads a single entry from the cache
        /// </summary>
        /// <param name="key">The key of the item to get from the cache</param>
        /// <returns>The data from the cache associated with the specified key</returns>
        public abstract object Get(string key);

        /// <summary>
        /// When overridden in a derived class, reads multiple entries from the cache
        /// </summary>
        /// <param name="keys">A list of keys for which the data is read</param>
        /// <returns>A keyed collection of items read from the cache</returns>
        public abstract IDictionary<string, object> Get(params string[] keys);

        /// <summary>
        /// When overridden in a derived class, reads a single entry from the cache and returns the data with detail information
        /// </summary>
        /// <param name="key">The key of the item to get from the cache</param>
        /// <returns>The data from the cache associated with the specified key with detail information</returns>
        public abstract CacheDetail GetWithDetail(string key);

        /// <summary>
        /// When overridden in a derived class, reads multiple entries from the cache with detail information
        /// </summary>
        /// <param name="keys">A list of keys for which the data is read</param>
        /// <returns>A keyed collection of items read from the cache with detail information</returns>
        public abstract IDictionary<string, CacheDetail> GetWithDetail(params string[] keys);

        /// <summary>
        /// When overridden in a derived class, increases a numeric cached value at the specified key by the specified amount
        /// </summary>
        /// <param name="key">The cache entry to increment</param>
        /// <param name="count">The amount by which the value is incremented</param>
        /// <returns>The new value of the data after the increment operation was carried out</returns>
        public abstract ulong Increment(string key, ulong count);

        /// <summary>
        /// When overridden in a derived class, adds data to an existing cache entry before the existing data
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <returns>The response code of the command sent to the server</returns>
        public abstract ResponseCode Prepend(string key, object data);

        /// <summary>
        /// When overridden in a derived class, replaces the data associated with an existing cache entry
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="expiration">The timestamp at wich the entry expires</param>
        /// <returns>The response code of the command sent to the server</returns>
        public abstract ResponseCode Replace(string key, object data, DateTime expiration);

        /// <summary>
        /// When overridden in a derived class, adds data to the cache, overwriting any data in an existing entry
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="expiration">The timestamp at wich the entry expires</param>
        /// <returns>The response code of the command sent to the server</returns>
        public abstract ResponseCode Set(string key, object data, DateTime expiration);

        //Public overloads
        /// <summary>
        /// Adds a key/value pair to a Memcached server if the data does not already exist.
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <returns>The response code of the command sent to the server</returns>
        public ResponseCode Add(string key, object data)
        {
            if (String.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key");
            }
            return this.Add(key, data, DateTime.MinValue);
        }

        /// <summary>
        /// Stores data for an existing key only if it has not been updated since the last get
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <param name="casUnique">The unique value of an an existing entry</param>
        /// <returns>The response code of the command sent to the server</returns>
        public ResponseCode CheckAndSet(string key, object data, ulong casUnique)
        {
            if (String.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key");
            }
            return this.CheckAndSet(key, data, DateTime.MinValue, casUnique);
        }

        /// <summary>
        /// Replaces the data associated with an existing cache entry
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <returns>The response code of the command sent to the server</returns>
        public ResponseCode Replace(string key, object data)
        {
            if (String.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key");
            }
            return this.Replace(key, data, DateTime.MinValue);
        }

        /// <summary>
        /// Adds data to the cache, overwriting any data in an existing entry
        /// </summary>
        /// <param name="key">The key to which the data is associated</param>
        /// <param name="data">The data to be stored in the cache</param>
        /// <returns>The response code of the command sent to the server</returns>
        public ResponseCode Set(string key, object data)
        {
            if (String.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key");
            }
            return this.Set(key, data, DateTime.MinValue);
        }
    }
}
